# bash/zsh completion support for core Git.
#
# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
# Distributed under the GNU General Public License, version 2.0.
#
# The contained completion routines provide support for completing:
#
#    *) local and remote branch names
#    *) local and remote tag names
#    *) .git/remotes file names
#    *) git 'subcommands'
#    *) tree paths within 'ref:path/to/file' expressions
#    *) file paths within current working directory and index
#    *) common --long-options
#
# To use these routines:
#
#    1) Copy this file to somewhere (e.g. ~/.git-completion.bash).
#    2) Add the following line to your .bashrc/.zshrc:
#        source ~/.git-completion.bash
#    3) Consider changing your PS1 to also show the current branch,
#       see git-prompt.sh for details.
#
# If you use complex aliases of form '!f() { ... }; f', you can use the null
# command ':' as the first command in the function body to declare the desired
# completion style.  For example '!f() { : git commit ; ... }; f' will
# tell the completion to use commit completion.  This also works with aliases
# of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".

case "$COMP_WORDBREAKS" in
*:*) : great ;;
*)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
esac

# __gitdir accepts 0 or 1 arguments (i.e., location)
# returns location of .git repo
__gitdir ()
{
    if [ -z "${1-}" ]; then
	if [ -n "${__git_dir-}" ]; then
	    echo "$__git_dir"
	    elif [ -n "${GIT_DIR-}" ]; then
	    test -d "${GIT_DIR-}" || return 1
	    echo "$GIT_DIR"
	    elif [ -d .git ]; then
	    echo .git
	    else
	    git rev-parse --git-dir 2>/dev/null
	    fi
	elif [ -d "$1/.git" ]; then
	echo "$1/.git"
	else
	echo "$1"
	fi
}

# The following function is based on code from:
#
#   bash_completion - programmable completion functions for bash 3.2+
#
#   Copyright © 2006-2008, Ian Macdonald <ian@caliban.org>
#             © 2009-2010, Bash Completion Maintainers
#                     <bash-completion-devel@lists.alioth.debian.org>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2, or (at your option)
#   any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software Foundation,
#   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#   The latest version of this software can be obtained here:
#
#   http://bash-completion.alioth.debian.org/
#
#   RELEASE: 2.x

# This function can be used to access a tokenized list of words
# on the command line:
#
#__git_reassemble_comp_words_by_ref '=:'
#if test "${words_[cword_-1]}" = -w
#then
#...
#fi
#
# The argument should be a collection of characters from the list of
# word completion separators (COMP_WORDBREAKS) to treat as ordinary
# characters.
#
# This is roughly equivalent to going back in time and setting
# COMP_WORDBREAKS to exclude those characters.  The intent is to
# make option types like --date=<type> and <rev>:<path> easy to
# recognize by treating each shell word as a single token.
#
# It is best not to set COMP_WORDBREAKS directly because the value is
# shared with other completion scripts.  By the time the completion
# function gets called, COMP_WORDS has already been populated so local
# changes to COMP_WORDBREAKS have no effect.
#
# Output: words_, cword_, cur_.

__git_reassemble_comp_words_by_ref()
{
    local exclude i j first
    # Which word separators to exclude?
    exclude="${1//[^$COMP_WORDBREAKS]}"
    cword_=$COMP_CWORD
    if [ -z "$exclude" ]; then
	words_=("${COMP_WORDS[@]}")
	return
	fi
    # List of word completion separators has shrunk;
    # re-assemble words to complete.
    for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do
	# Append each nonempty word consisting of just
	# word separator characters to the current word.
	first=t
	while
	[ $i -gt 0 ] &&
	[ -n "${COMP_WORDS[$i]}" ] &&
	# word consists of excluded word separators
	[ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ]
	do
	    # Attach to the previous token,
	    # unless the previous token is the command name.
	    if [ $j -ge 2 ] && [ -n "$first" ]; then
		((j--))
		fi
	    first=
	    words_[$j]=${words_[j]}${COMP_WORDS[i]}
	    if [ $i = $COMP_CWORD ]; then
		cword_=$j
		fi
	    if (($i < ${#COMP_WORDS[@]} - 1)); then
		((i++))
		else
		# Done.
		return
		fi
	    done
	words_[$j]=${words_[j]}${COMP_WORDS[i]}
	if [ $i = $COMP_CWORD ]; then
	    cword_=$j
	    fi
	done
}

if ! type _get_comp_words_by_ref >/dev/null 2>&1; then
_get_comp_words_by_ref ()
{
    local exclude cur_ words_ cword_
    if [ "$1" = "-n" ]; then
	exclude=$2
	shift 2
	fi
    __git_reassemble_comp_words_by_ref "$exclude"
    cur_=${words_[cword_]}
    while [ $# -gt 0 ]; do
	case "$1" in
	    cur)
		cur=$cur_
		;;
	    prev)
		prev=${words_[$cword_-1]}
		;;
	    words)
		words=("${words_[@]}")
		;;
	    cword)
		cword=$cword_
		;;
	    esac
	shift
	done
}
fi

__gitcompappend ()
{
    local x i=${#COMPREPLY[@]}
    for x in $1; do
	if [[ "$x" == "$3"* ]]; then
	    COMPREPLY[i++]="$2$x$4"
	    fi
	done
}

__gitcompadd ()
{
    COMPREPLY=()
    __gitcompappend "$@"
}

# Generates completion reply, appending a space to possible completion words,
# if necessary.
# It accepts 1 to 4 arguments:
# 1: List of possible completion words.
# 2: A prefix to be added to each possible completion word (optional).
# 3: Generate possible completion matches for this word (optional).
# 4: A suffix to be appended to each possible completion word (optional).
__gitcomp ()
{
    local cur_="${3-$cur}"

    case "$cur_" in
	--*=)
	    ;;
	*)
	    local c i=0 IFS=$' \t\n'
	    for c in $1; do
		c="$c${4-}"
		if [[ $c == "$cur_"* ]]; then
		    case $c in
			--*=*|*.) ;;
			*) c="$c " ;;
			esac
		    COMPREPLY[i++]="${2-}$c"
		    fi
		done
	    ;;
	esac
}

# Variation of __gitcomp_nl () that appends to the existing list of
# completion candidates, COMPREPLY.
__gitcomp_nl_append ()
{
    local IFS=$'\n'
    __gitcompappend "$1" "${2-}" "${3-$cur}" "${4- }"
}

# Generates completion reply from newline-separated possible completion words
# by appending a space to all of them.
# It accepts 1 to 4 arguments:
# 1: List of possible completion words, separated by a single newline.
# 2: A prefix to be added to each possible completion word (optional).
# 3: Generate possible completion matches for this word (optional).
# 4: A suffix to be appended to each possible completion word instead of
#    the default space (optional).  If specified but empty, nothing is
#    appended.
__gitcomp_nl ()
{
    COMPREPLY=()
    __gitcomp_nl_append "$@"
}

# Generates completion reply with compgen from newline-separated possible
# completion filenames.
# It accepts 1 to 3 arguments:
# 1: List of possible completion filenames, separated by a single newline.
# 2: A directory prefix to be added to each possible completion filename
#    (optional).
# 3: Generate possible completion matches for this word (optional).
__gitcomp_file ()
{
    local IFS=$'\n'

    # XXX does not work when the directory prefix contains a tilde,
    # since tilde expansion is not applied.
    # This means that COMPREPLY will be empty and Bash default
    # completion will be used.
    __gitcompadd "$1" "${2-}" "${3-$cur}" ""

    # use a hack to enable file mode in bash < 4
    compopt -o filenames +o nospace 2>/dev/null ||
    compgen -f /non-existing-dir/ > /dev/null
}

# Execute 'git ls-files', unless the --committable option is specified, in
# which case it runs 'git diff-index' to find out the files that can be
# committed.  It return paths relative to the directory specified in the first
# argument, and using the options specified in the second argument.
__git_ls_files_helper ()
{
    if [ "$2" == "--committable" ]; then
	git -C "$1" diff-index --name-only --relative HEAD
	else
	# NOTE: $2 is not quoted in order to support multiple options
	git -C "$1" ls-files --exclude-standard $2
	fi 2>/dev/null
}


# __git_index_files accepts 1 or 2 arguments:
# 1: Options to pass to ls-files (required).
# 2: A directory path (optional).
#    If provided, only files within the specified directory are listed.
#    Sub directories are never recursed.  Path must have a trailing
#    slash.
__git_index_files ()
{
    local dir="$(__gitdir)" root="${2-.}" file

    if [ -d "$dir" ]; then
	__git_ls_files_helper "$root" "$1" |
	while read -r file; do
	    case "$file" in
		?*/*) echo "${file%%/*}" ;;
		*) echo "$file" ;;
		esac
	    done | sort | uniq
	fi
}

__git_heads ()
{
    local dir="$(__gitdir)"
    if [ -d "$dir" ]; then
	git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
	    refs/heads
	return
	fi
}

__git_tags ()
{
    local dir="$(__gitdir)"
    if [ -d "$dir" ]; then
	git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
	    refs/tags
	return
	fi
}

# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments
# presence of 2nd argument means use the guess heuristic employed
# by checkout for tracking branches
__git_refs ()
{
    local i hash dir="$(__gitdir "${1-}")" track="${2-}"
    local format refs
    if [ -d "$dir" ]; then
	case "$cur" in
	    refs|refs/*)
		format="refname"
		refs="${cur%/*}"
		track=""
		;;
	    *)
		for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
		    if [ -e "$dir/$i" ]; then echo $i; fi
		    done
		format="refname:short"
		refs="refs/tags refs/heads refs/remotes"
		;;
	    esac
	git --git-dir="$dir" for-each-ref --format="%($format)" \
	    $refs
	if [ -n "$track" ]; then
	    # employ the heuristic used by git checkout
	    # Try to find a remote branch that matches the completion word
	    # but only output if the branch name is unique
	    local ref entry
	    git --git-dir="$dir" for-each-ref --shell --format="ref=%(refname:short)" \
		"refs/remotes/" | \
		while read -r entry; do
		eval "$entry"
		ref="${ref#*/}"
		if [[ "$ref" == "$cur"* ]]; then
		    echo "$ref"
		    fi
		done | sort | uniq -u
	    fi
	return
	fi
    case "$cur" in
	refs|refs/*)
	    git ls-remote "$dir" "$cur*" 2>/dev/null | \
		while read -r hash i; do
		case "$i" in
		    *^{}) ;;
		    *) echo "$i" ;;
		    esac
		done
	    ;;
	*)
	    echo "HEAD"
	    git for-each-ref --format="%(refname:short)" -- \
		"refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
	    ;;
	esac
}

# __git_refs2 requires 1 argument (to pass to __git_refs)
__git_refs2 ()
{
    local i
    for i in $(__git_refs "$1"); do
	echo "$i:$i"
	done
}

# __git_refs_remotes requires 1 argument (to pass to ls-remote)
__git_refs_remotes ()
{
    local i hash
    git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
	while read -r hash i; do
	    echo "$i:refs/remotes/$1/${i#refs/heads/}"
	    done
}

__git_remotes ()
{
    local d="$(__gitdir)"
    test -d "$d/remotes" && ls -1 "$d/remotes"
    git --git-dir="$d" remote
}

__git_list_merge_strategies ()
{
    git merge -s help 2>&1 |
    sed -n -e '/[Aa]vailable strategies are: /,/^$/{
s/\.$//
s/.*://
s/^[ ]*//
s/[ ]*$//
p
}'
}

__git_merge_strategies=
# 'git merge -s help' (and thus detection of the merge strategy
# list) fails, unfortunately, if run outside of any git working
# tree.  __git_merge_strategies is set to the empty string in
# that case, and the detection will be repeated the next time it
# is needed.
__git_compute_merge_strategies ()
{
    test -n "$__git_merge_strategies" ||
    __git_merge_strategies=$(__git_list_merge_strategies)
}

__git_complete_revlist_file ()
{
    local pfx ls ref cur_="$cur"
    case "$cur_" in
	*..?*:*)
	    return
	    ;;
	?*:*)
	    ref="${cur_%%:*}"
	    cur_="${cur_#*:}"
	    case "$cur_" in
		?*/*)
		    pfx="${cur_%/*}"
		    cur_="${cur_##*/}"
		    ls="$ref:$pfx"
		    pfx="$pfx/"
		    ;;
		*)
		    ls="$ref"
		    ;;
		esac

	    case "$COMP_WORDBREAKS" in
		*:*) : great ;;
		*)   pfx="$ref:$pfx" ;;
		esac

	    __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
| sed '/^100... blob /{
           s,^.*,,
           s,$, ,
       }
       /^120000 blob /{
           s,^.*,,
           s,$, ,
       }
       /^040000 tree /{
           s,^.*,,
           s,$,/,
       }
       s/^.*//')" \
	   "$pfx" "$cur_" ""
	    ;;
	*...*)
	    pfx="${cur_%...*}..."
	    cur_="${cur_#*...}"
	    __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
	    ;;
	*..*)
	    pfx="${cur_%..*}.."
	    cur_="${cur_#*..}"
	    __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
	    ;;
	*)
	    __gitcomp_nl "$(__git_refs)"
	    ;;
	esac
}


# __git_complete_index_file requires 1 argument:
# 1: the options to pass to ls-file
#
# The exception is --committable, which finds the files appropriate commit.
__git_complete_index_file ()
{
    local pfx="" cur_="$cur"

    case "$cur_" in
	?*/*)
	    pfx="${cur_%/*}"
	    cur_="${cur_##*/}"
	    pfx="${pfx}/"
	    ;;
	esac

    __gitcomp_file "$(__git_index_files "$1" ${pfx:+"$pfx"})" "$pfx" "$cur_"
}

__git_complete_file ()
{
    __git_complete_revlist_file
}

__git_complete_revlist ()
{
    __git_complete_revlist_file
}

__git_complete_remote_or_refspec ()
{
    local cur_="$cur" cmd="${words[1]}"
    local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
    if [ "$cmd" = "remote" ]; then
	((c++))
	fi
    while [ $c -lt $cword ]; do
	i="${words[c]}"
	case "$i" in
	    --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
	    --all)
		case "$cmd" in
		    push) no_complete_refspec=1 ;;
		    fetch)
			return
			;;
		    *) ;;
		    esac
		;;
	    -*) ;;
	    *) remote="$i"; break ;;
	    esac
	((c++))
	done
    if [ -z "$remote" ]; then
	__gitcomp_nl "$(__git_remotes)"
	return
	fi
    if [ $no_complete_refspec = 1 ]; then
	return
	fi
    [ "$remote" = "." ] && remote=
    case "$cur_" in
	*:*)
	    case "$COMP_WORDBREAKS" in
		*:*) : great ;;
		*)   pfx="${cur_%%:*}:" ;;
		esac
	    cur_="${cur_#*:}"
	    lhs=0
	    ;;
	+*)
	    pfx="+"
	    cur_="${cur_#+}"
	    ;;
	esac
    case "$cmd" in
	fetch)
	    if [ $lhs = 1 ]; then
		__gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
		else
		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
		fi
	    ;;
	pull|remote)
	    if [ $lhs = 1 ]; then
		__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
		else
		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
		fi
	    ;;
	push)
	    if [ $lhs = 1 ]; then
		__gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
		else
		__gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
		fi
	    ;;
	esac
}

__git_complete_strategy ()
{
    __git_compute_merge_strategies
    case "$prev" in
	-s|--strategy)
	    __gitcomp "$__git_merge_strategies"
	    return 0
	    esac
    case "$cur" in
	--strategy=*)
	    __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
	    return 0
	    ;;
	esac
    return 1
}

__git_commands () {
    if test -n "${GIT_TESTING_COMMAND_COMPLETION:-}"
	then
	printf "%s" "${GIT_TESTING_COMMAND_COMPLETION}"
	else
	git help -a|egrep '^  [a-zA-Z0-9]'
	fi
}

__git_list_all_commands ()
{
    local i IFS=" "$'\n'
    for i in $(__git_commands)
    do
	case $i in
	    *--*)             : helper pattern;;
	    *) echo $i;;
	    esac
	done
}

__git_all_commands=
__git_compute_all_commands ()
{
    test -n "$__git_all_commands" ||
    __git_all_commands=$(__git_list_all_commands)
}

__git_list_porcelain_commands ()
{
    local i IFS=" "$'\n'
    __git_compute_all_commands
    for i in $__git_all_commands
    do
	case $i in
	    *--*)             : helper pattern;;
	    applymbox)        : ask gittus;;
	    applypatch)       : ask gittus;;
	    archimport)       : import;;
	    cat-file)         : plumbing;;
	    check-attr)       : plumbing;;
	    check-ignore)     : plumbing;;
	    check-mailmap)    : plumbing;;
	    check-ref-format) : plumbing;;
	    checkout-index)   : plumbing;;
	    commit-tree)      : plumbing;;
	    count-objects)    : infrequent;;
	    credential-cache) : credentials helper;;
	    credential-store) : credentials helper;;
	    cvsexportcommit)  : export;;
	    cvsimport)        : import;;
	    cvsserver)        : daemon;;
	    daemon)           : daemon;;
	    diff-files)       : plumbing;;
	    diff-index)       : plumbing;;
	    diff-tree)        : plumbing;;
	    fast-import)      : import;;
	    fast-export)      : export;;
	    fsck-objects)     : plumbing;;
	    fetch-pack)       : plumbing;;
	    fmt-merge-msg)    : plumbing;;
	    for-each-ref)     : plumbing;;
	    hash-object)      : plumbing;;
	    http-*)           : transport;;
	    index-pack)       : plumbing;;
	    init-db)          : deprecated;;
	    local-fetch)      : plumbing;;
	    ls-files)         : plumbing;;
	    ls-remote)        : plumbing;;
	    ls-tree)          : plumbing;;
	    mailinfo)         : plumbing;;
	    mailsplit)        : plumbing;;
	    merge-*)          : plumbing;;
	    mktree)           : plumbing;;
	    mktag)            : plumbing;;
	    pack-objects)     : plumbing;;
	    pack-redundant)   : plumbing;;
	    pack-refs)        : plumbing;;
	    parse-remote)     : plumbing;;
	    patch-id)         : plumbing;;
	    prune)            : plumbing;;
	    prune-packed)     : plumbing;;
	    quiltimport)      : import;;
	    read-tree)        : plumbing;;
	    receive-pack)     : plumbing;;
	    remote-*)         : transport;;
	    rerere)           : plumbing;;
	    rev-list)         : plumbing;;
	    rev-parse)        : plumbing;;
	    runstatus)        : plumbing;;
	    sh-setup)         : internal;;
	    shell)            : daemon;;
	    show-ref)         : plumbing;;
	    send-pack)        : plumbing;;
	    show-index)       : plumbing;;
	    ssh-*)            : transport;;
	    stripspace)       : plumbing;;
	    symbolic-ref)     : plumbing;;
	    unpack-file)      : plumbing;;
	    unpack-objects)   : plumbing;;
	    update-index)     : plumbing;;
	    update-ref)       : plumbing;;
	    update-server-info) : daemon;;
	    upload-archive)   : plumbing;;
	    upload-pack)      : plumbing;;
	    write-tree)       : plumbing;;
	    var)              : infrequent;;
	    verify-pack)      : infrequent;;
	    verify-tag)       : plumbing;;
	    *) echo $i;;
	    esac
	done
}

__git_porcelain_commands=
__git_compute_porcelain_commands ()
{
    test -n "$__git_porcelain_commands" ||
    __git_porcelain_commands=$(__git_list_porcelain_commands)
}

__git_pretty_aliases ()
{
    local i IFS=$'\n'
    for i in $(git --git-dir="$(__gitdir)" config --get-regexp "pretty\..*" 2>/dev/null); do
	case "$i" in
	    pretty.*)
		i="${i#pretty.}"
		echo "${i/ */}"
		;;
	    esac
	done
}

__git_aliases ()
{
    local i IFS=$'\n'
    for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
	case "$i" in
	    alias.*)
		i="${i#alias.}"
		echo "${i/ */}"
		;;
	    esac
	done
}

# __git_aliased_command requires 1 argument
__git_aliased_command ()
{
    local word cmdline=$(git --git-dir="$(__gitdir)" \
	config --get "alias.$1")
    for word in $cmdline; do
	case "$word" in
	    \!gitk|gitk)
		echo "gitk"
		return
		;;
	    \!*): shell command alias ;;
	    -*): option ;;
	    *=*): setting env ;;
	    git): git itself ;;
	    \(\))   : skip parens of shell function definition ;;
	    {): skip start of shell helper function ;;
	    :): skip null command ;;
	    \'*): skip opening quote after sh -c ;;
	    *)
		echo "$word"
		return
		esac
	done
}

# __git_find_on_cmdline requires 1 argument
__git_find_on_cmdline ()
{
    local word subcommand c=1
    while [ $c -lt $cword ]; do
	word="${words[c]}"
	for subcommand in $1; do
	    if [ "$subcommand" = "$word" ]; then
		echo "$subcommand"
		return
		fi
	    done
	((c++))
	done
}

__git_has_doubledash ()
{
    local c=1
    while [ $c -lt $cword ]; do
	if [ "--" = "${words[c]}" ]; then
	    return 0
	    fi
	((c++))
	done
    return 1
}

# Try to count non option arguments passed on the command line for the
# specified git command.
# When options are used, it is necessary to use the special -- option to
# tell the implementation were non option arguments begin.
# XXX this can not be improved, since options can appear everywhere, as
# an example:
#git mv x -n y
#
# __git_count_arguments requires 1 argument: the git command executed.
__git_count_arguments ()
{
    local word i c=0

    # Skip "git" (first argument)
    for ((i=1; i < ${#words[@]}; i++)); do
	word="${words[i]}"

	case "$word" in
	    --)
		# Good; we can assume that the following are only non
		# option arguments.
		((c = 0))
		;;
	    "$1")
		# Skip the specified git command and discard git
		# main options
		((c = 0))
		;;
	    ?*)
		((c++))
		;;
	    esac
	done

    printf "%d" $c
}

__git_whitespacelist="nowarn warn error error-all fix"

_git_am ()
{
    local dir="$(__gitdir)"
    if [ -d "$dir"/rebase-apply ]; then
	__gitcomp "--skip --continue --resolved --abort"
	return
	fi
    case "$cur" in
	--whitespace=*)
	    __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
	    return
	    ;;
	--*)
	    __gitcomp "
--3way --committer-date-is-author-date --ignore-date
--ignore-whitespace --ignore-space-change
--interactive --keep --no-utf8 --signoff --utf8
--whitespace= --scissors
"
	    return
	    esac
}

_git_apply ()
{
    case "$cur" in
	--whitespace=*)
	    __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
	    return
	    ;;
	--*)
	    __gitcomp "
--stat --numstat --summary --check --index
--cached --index-info --reverse --reject --unidiff-zero
--apply --no-add --exclude=
--ignore-whitespace --ignore-space-change
--whitespace= --inaccurate-eof --verbose
"
	    return
	    esac
}

_git_add ()
{
    case "$cur" in
	--*)
	    __gitcomp "
--interactive --refresh --patch --update --dry-run
--ignore-errors --intent-to-add
"
	    return
	    esac

    # XXX should we check for --update and --all options ?
    __git_complete_index_file "--others --modified --directory --no-empty-directory"
}

_git_archive ()
{
    case "$cur" in
	--format=*)
	    __gitcomp "$(git archive --list)" "" "${cur##--format=}"
	    return
	    ;;
	--remote=*)
	    __gitcomp_nl "$(__git_remotes)" "" "${cur##--remote=}"
	    return
	    ;;
	--*)
	    __gitcomp "
--format= --list --verbose
--prefix= --remote= --exec=
"
	    return
	    ;;
	esac
    __git_complete_file
}

_git_bisect ()
{
    __git_has_doubledash && return

    local subcommands="start bad good skip reset visualize replay log run"
    local subcommand="$(__git_find_on_cmdline "$subcommands")"
    if [ -z "$subcommand" ]; then
	if [ -f "$(__gitdir)"/BISECT_START ]; then
	    __gitcomp "$subcommands"
	    else
	    __gitcomp "replay start"
	    fi
	return
	fi

    case "$subcommand" in
	bad|good|reset|skip|start)
	    __gitcomp_nl "$(__git_refs)"
	    ;;
	*)
	    ;;
	esac
}

_git_branch ()
{
    local i c=1 only_local_ref="n" has_r="n"

    while [ $c -lt $cword ]; do
	i="${words[c]}"
	case "$i" in
	    -d|-m)only_local_ref="y" ;;
	    -r)has_r="y" ;;
	    esac
	((c++))
	done

    case "$cur" in
	--set-upstream-to=*)
	    __gitcomp_nl "$(__git_refs)" "" "${cur##--set-upstream-to=}"
	    ;;
	--*)
	    __gitcomp "
--color --no-color --verbose --abbrev= --no-abbrev
--track --no-track --contains --merged --no-merged
--set-upstream-to= --edit-description --list
--unset-upstream
"
	    ;;
	*)
	    if [ $only_local_ref = "y" -a $has_r = "n" ]; then
		__gitcomp_nl "$(__git_heads)"
		else
		__gitcomp_nl "$(__git_refs)"
		fi
	    ;;
	esac
}

_git_bundle ()
{
    local cmd="${words[2]}"
    case "$cword" in
	2)
	    __gitcomp "create list-heads verify unbundle"
	    ;;
	3)
	    # looking for a file
	    ;;
	*)
	    case "$cmd" in
		create)
		    __git_complete_revlist
		    ;;
		esac
	    ;;
	esac
}

_git_checkout ()
{
    __git_has_doubledash && return

    case "$cur" in
	--conflict=*)
	    __gitcomp "diff3 merge" "" "${cur##--conflict=}"
	    ;;
	--*)
	    __gitcomp "
--quiet --ours --theirs --track --no-track --merge
--conflict= --orphan --patch
"
	    ;;
	*)
	    # check if --track, --no-track, or --no-guess was specified
	    # if so, disable DWIM mode
	    local flags="--track --no-track --no-guess" track=1
	    if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
		track=''
		fi
	    __gitcomp_nl "$(__git_refs '' $track)"
	    ;;
	esac
}

_git_cherry ()
{
    __gitcomp_nl "$(__git_refs)"
}

_git_cherry_pick ()
{
    local dir="$(__gitdir)"
    if [ -f "$dir"/CHERRY_PICK_HEAD ]; then
	__gitcomp "--continue --quit --abort"
	return
	fi
    case "$cur" in
	--*)
	    __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
	    ;;
	*)
	    __gitcomp_nl "$(__git_refs)"
	    ;;
	esac
}

_git_clean ()
{
    case "$cur" in
	--*)
	    __gitcomp "--dry-run --quiet"
	    return
	    ;;
	esac

    # XXX should we check for -x option ?
    __git_complete_index_file "--others --directory"
}

_git_clone ()
{
    case "$cur" in
	--*)
	    __gitcomp "
--local
--no-hardlinks
--shared
--reference
--quiet
--no-checkout
--bare
--mirror
--origin
--upload-pack
--template=
--depth
--single-branch
--branch
"
	    return
	    ;;
	esac
}

_git_commit ()
{
    case "$prev" in
	-c|-C)
	    __gitcomp_nl "$(__git_refs)" "" "${cur}"
	    return
	    ;;
	esac

    case "$cur" in
	--cleanup=*)
	    __gitcomp "default strip verbatim whitespace
" "" "${cur##--cleanup=}"
	    return
	    ;;
	--reuse-message=*|--reedit-message=*|\
--fixup=*|--squash=*)
	    __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
	    return
	    ;;
	--untracked-files=*)
	    __gitcomp "all no normal" "" "${cur##--untracked-files=}"
	    return
	    ;;
	--*)
	    __gitcomp "
--all --author= --signoff --verify --no-verify
--edit --no-edit
--amend --include --only --interactive
--dry-run --reuse-message= --reedit-message=
--reset-author --file= --message= --template=
--cleanup= --untracked-files --untracked-files=
--verbose --quiet --fixup= --squash=
"
	    return
	    esac

    if git rev-parse --verify --quiet HEAD >/dev/null; then
	__git_complete_index_file "--committable"
	else
	# This is the first commit
	__git_complete_index_file "--cached"
	fi
}

_git_describe ()
{
    case "$cur" in
	--*)
	    __gitcomp "
--all --tags --contains --abbrev= --candidates=
--exact-match --debug --long --match --always
"
	    return
	    esac
    __gitcomp_nl "$(__git_refs)"
}

__git_diff_algorithms="myers minimal patience histogram"

__git_diff_common_options="--stat --numstat --shortstat --summary
--patch-with-stat --name-only --name-status --color
--no-color --color-words --no-renames --check
--full-index --binary --abbrev --diff-filter=
--find-copies-harder
--text --ignore-space-at-eol --ignore-space-change
--ignore-all-space --ignore-blank-lines --exit-code
--quiet --ext-diff --no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
--inter-hunk-context=
--patience --histogram --minimal
--raw --word-diff
--dirstat --dirstat= --dirstat-by-file
--dirstat-by-file= --cumulative
--diff-algorithm=
"

_git_diff ()
{
    __git_has_doubledash && return

    case "$cur" in
	--diff-algorithm=*)
	    __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
	    return
	    ;;
	--*)
	    __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
--base --ours --theirs --no-index
$__git_diff_common_options
"
	    return
	    ;;
	esac
    __git_complete_revlist_file
}

__git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
"

_git_difftool ()
{
    __git_has_doubledash && return

    case "$cur" in
	--tool=*)
	    __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
	    return
	    ;;
	--*)
	    __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
--base --ours --theirs
--no-renames --diff-filter= --find-copies-harder
--relative --ignore-submodules
--tool="
	    return
	    ;;
	esac
    __git_complete_revlist_file
}

__git_fetch_recurse_submodules="yes on-demand no"

__git_fetch_options="
--quiet --verbose --append --upload-pack --force --keep --depth=
--tags --no-tags --all --prune --dry-run --recurse-submodules=
"

_git_fetch ()
{
    case "$cur" in
	--recurse-submodules=*)
	    __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
	    return
	    ;;
	--*)
	    __gitcomp "$__git_fetch_options"
	    return
	    ;;
	esac
    __git_complete_remote_or_refspec
}

__git_format_patch_options="
--stdout --attach --no-attach --thread --thread= --no-thread
--numbered --start-number --numbered-files --keep-subject --signoff
--signature --no-signature --in-reply-to= --cc= --full-index --binary
--not --all --cover-letter --no-prefix --src-prefix= --dst-prefix=
--inline --suffix= --ignore-if-in-upstream --subject-prefix=
--output-directory --reroll-count --to= --quiet --notes
"

_git_format_patch ()
{
    case "$cur" in
	--thread=*)
	    __gitcomp "
deep shallow
" "" "${cur##--thread=}"
	    return
	    ;;
	--*)
	    __gitcomp "$__git_format_patch_options"
	    return
	    ;;
	esac
    __git_complete_revlist
}

_git_fsck ()
{
    case "$cur" in
	--*)
	    __gitcomp "
--tags --root --unreachable --cache --no-reflogs --full
--strict --verbose --lost-found
"
	    return
	    ;;
	esac
}

_git_gc ()
{
    case "$cur" in
	--*)
	    __gitcomp "--prune --aggressive"
	    return
	    ;;
	esac
}

_git_gitk ()
{
    _gitk
}

__git_match_ctag() {
    awk "/^${1//\//\\/}/ { print \$1 }" "$2"
}

_git_grep ()
{
    __git_has_doubledash && return

    case "$cur" in
	--*)
	    __gitcomp "
--cached
--text --ignore-case --word-regexp --invert-match
--full-name --line-number
--extended-regexp --basic-regexp --fixed-strings
--perl-regexp
--files-with-matches --name-only
--files-without-match
--max-depth
--count
--and --or --not --all-match
"
	    return
	    ;;
	esac

    case "$cword,$prev" in
	2,*|*,-*)
	    if test -r tags; then
		__gitcomp_nl "$(__git_match_ctag "$cur" tags)"
		return
		fi
	    ;;
	esac

    __gitcomp_nl "$(__git_refs)"
}

_git_help ()
{
    case "$cur" in
	--*)
	    __gitcomp "--all --info --man --web"
	    return
	    ;;
	esac
    __git_compute_all_commands
    __gitcomp "$__git_all_commands $(__git_aliases)
attributes cli core-tutorial cvs-migration
diffcore gitk glossary hooks ignore modules
namespaces repository-layout tutorial tutorial-2
workflows
"
}

_git_init ()
{
    case "$cur" in
	--shared=*)
	    __gitcomp "
false true umask group all world everybody
" "" "${cur##--shared=}"
	    return
	    ;;
	--*)
	    __gitcomp "--quiet --bare --template= --shared --shared="
	    return
	    ;;
	esac
}

_git_ls_files ()
{
    case "$cur" in
	--*)
	    __gitcomp "--cached --deleted --modified --others --ignored
--stage --directory --no-empty-directory --unmerged
--killed --exclude= --exclude-from=
--exclude-per-directory= --exclude-standard
--error-unmatch --with-tree= --full-name
--abbrev --ignored --exclude-per-directory
"
	    return
	    ;;
	esac

    # XXX ignore options like --modified and always suggest all cached
    # files.
    __git_complete_index_file "--cached"
}

_git_ls_remote ()
{
    __gitcomp_nl "$(__git_remotes)"
}

_git_ls_tree ()
{
    __git_complete_file
}

# Options that go well for log, shortlog and gitk
__git_log_common_options="
--not --all
--branches --tags --remotes
--first-parent --merges --no-merges
--max-count=
--max-age= --since= --after=
--min-age= --until= --before=
--min-parents= --max-parents=
--no-min-parents --no-max-parents
"
# Options that go well for log and gitk (not shortlog)
__git_log_gitk_options="
--dense --sparse --full-history
--simplify-merges --simplify-by-decoration
--left-right --notes --no-notes
"
# Options that go well for log and shortlog (not gitk)
__git_log_shortlog_options="
--author= --committer= --grep=
--all-match --invert-grep
"

__git_log_pretty_formats="oneline short medium full fuller email raw format:"
__git_log_date_formats="relative iso8601 rfc2822 short local default raw"

_git_log ()
{
    __git_has_doubledash && return

    local g="$(git rev-parse --git-dir 2>/dev/null)"
    local merge=""
    if [ -f "$g/MERGE_HEAD" ]; then
	merge="--merge"
	fi
    case "$cur" in
	--pretty=*|--format=*)
	    __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
" "" "${cur#*=}"
	    return
	    ;;
	--date=*)
	    __gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
	    return
	    ;;
	--decorate=*)
	    __gitcomp "full short no" "" "${cur##--decorate=}"
	    return
	    ;;
	--*)
	    __gitcomp "
$__git_log_common_options
$__git_log_shortlog_options
$__git_log_gitk_options
--root --topo-order --date-order --reverse
--follow --full-diff
--abbrev-commit --abbrev=
--relative-date --date=
--pretty= --format= --oneline
--show-signature
--cherry-pick
--graph
--decorate --decorate=
--walk-reflogs
--parents --children
$merge
$__git_diff_common_options
--pickaxe-all --pickaxe-regex
"
	    return
	    ;;
	esac
    __git_complete_revlist
}

# Common merge options shared by git-merge(1) and git-pull(1).
__git_merge_options="
--no-commit --no-stat --log --no-log --squash --strategy
--commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
--verify-signatures --no-verify-signatures --gpg-sign
--quiet --verbose --progress --no-progress
"

_git_merge ()
{
    __git_complete_strategy && return

    case "$cur" in
	--*)
	    __gitcomp "$__git_merge_options
--rerere-autoupdate --no-rerere-autoupdate --abort"
	    return
	    esac
    __gitcomp_nl "$(__git_refs)"
}

_git_mergetool ()
{
    case "$cur" in
	--tool=*)
	    __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
	    return
	    ;;
	--*)
	    __gitcomp "--tool="
	    return
	    ;;
	esac
}

_git_merge_base ()
{
    case "$cur" in
	--*)
	    __gitcomp "--octopus --independent --is-ancestor --fork-point"
	    return
	    ;;
	esac
    __gitcomp_nl "$(__git_refs)"
}

_git_mv ()
{
    case "$cur" in
	--*)
	    __gitcomp "--dry-run"
	    return
	    ;;
	esac

    if [ $(__git_count_arguments "mv") -gt 0 ]; then
	# We need to show both cached and untracked files (including
	# empty directories) since this may not be the last argument.
	__git_complete_index_file "--cached --others --directory"
	else
	__git_complete_index_file "--cached"
	fi
}

_git_name_rev ()
{
    __gitcomp "--tags --all --stdin"
}

_git_notes ()
{
    local subcommands='add append copy edit list prune remove show'
    local subcommand="$(__git_find_on_cmdline "$subcommands")"

    case "$subcommand,$cur" in
	,--*)
	    __gitcomp '--ref'
	    ;;
	,*)
	    case "$prev" in
		--ref)
		    __gitcomp_nl "$(__git_refs)"
		    ;;
		*)
		    __gitcomp "$subcommands --ref"
		    ;;
		esac
	    ;;
	add,--reuse-message=*|append,--reuse-message=*|\
add,--reedit-message=*|append,--reedit-message=*)
	    __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
	    ;;
	add,--*|append,--*)
	    __gitcomp '--file= --message= --reedit-message=
--reuse-message='
	    ;;
	copy,--*)
	    __gitcomp '--stdin'
	    ;;
	prune,--*)
	    __gitcomp '--dry-run --verbose'
	    ;;
	prune,*)
	    ;;
	*)
	    case "$prev" in
		-m|-F)
		    ;;
		*)
		    __gitcomp_nl "$(__git_refs)"
		    ;;
		esac
	    ;;
	esac
}

_git_pull ()
{
    __git_complete_strategy && return

    case "$cur" in
	--recurse-submodules=*)
	    __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
	    return
	    ;;
	--*)
	    __gitcomp "
--rebase --no-rebase
$__git_merge_options
$__git_fetch_options
"
	    return
	    ;;
	esac
    __git_complete_remote_or_refspec
}

__git_push_recurse_submodules="check on-demand"

__git_complete_force_with_lease ()
{
    local cur_=$1

    case "$cur_" in
	--*=)
	    ;;
	*:*)
	    __gitcomp_nl "$(__git_refs)" "" "${cur_#*:}"
	    ;;
	*)
	    __gitcomp_nl "$(__git_refs)" "" "$cur_"
	    ;;
	esac
}

_git_push ()
{
    case "$prev" in
	--repo)
	    __gitcomp_nl "$(__git_remotes)"
	    return
	    ;;
	--recurse-submodules)
	    __gitcomp "$__git_push_recurse_submodules"
	    return
	    ;;
	esac
    case "$cur" in
	--repo=*)
	    __gitcomp_nl "$(__git_remotes)" "" "${cur##--repo=}"
	    return
	    ;;
	--recurse-submodules=*)
	    __gitcomp "$__git_push_recurse_submodules" "" "${cur##--recurse-submodules=}"
	    return
	    ;;
	--force-with-lease=*)
	    __git_complete_force_with_lease "${cur##--force-with-lease=}"
	    return
	    ;;
	--*)
	    __gitcomp "
--all --mirror --tags --dry-run --force --verbose
--quiet --prune --delete --follow-tags
--receive-pack= --repo= --set-upstream
--force-with-lease --force-with-lease= --recurse-submodules=
"
	    return
	    ;;
	esac
    __git_complete_remote_or_refspec
}

_git_rebase ()
{
    local dir="$(__gitdir)"
    if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
	__gitcomp "--continue --skip --abort"
	return
	fi
    __git_complete_strategy && return
    case "$cur" in
	--whitespace=*)
	    __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
	    return
	    ;;
	--*)
	    __gitcomp "
--onto --merge --strategy --interactive
--preserve-merges --stat --no-stat
--committer-date-is-author-date --ignore-date
--ignore-whitespace --whitespace=
--autosquash --fork-point --no-fork-point
--autostash
"

	    return
	    esac
    __gitcomp_nl "$(__git_refs)"
}

_git_reflog ()
{
    local subcommands="show delete expire"
    local subcommand="$(__git_find_on_cmdline "$subcommands")"

    if [ -z "$subcommand" ]; then
	__gitcomp "$subcommands"
	else
	__gitcomp_nl "$(__git_refs)"
	fi
}

__git_send_email_confirm_options="always never auto cc compose"
__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"

_git_send_email ()
{
    case "$cur" in
	--confirm=*)
	    __gitcomp "
$__git_send_email_confirm_options
" "" "${cur##--confirm=}"
	    return
	    ;;
	--suppress-cc=*)
	    __gitcomp "
$__git_send_email_suppresscc_options
" "" "${cur##--suppress-cc=}"

	    return
	    ;;
	--smtp-encryption=*)
	    __gitcomp "ssl tls" "" "${cur##--smtp-encryption=}"
	    return
	    ;;
	--thread=*)
	    __gitcomp "
deep shallow
" "" "${cur##--thread=}"
	    return
	    ;;
	--*)
	    __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
--compose --confirm= --dry-run --envelope-sender
--from --identity
--in-reply-to --no-chain-reply-to --no-signed-off-by-cc
--no-suppress-from --no-thread --quiet
--signed-off-by-cc --smtp-pass --smtp-server
--smtp-server-port --smtp-encryption= --smtp-user
--subject --suppress-cc= --suppress-from --thread --to
--validate --no-validate
$__git_format_patch_options"
	    return
	    ;;
	esac
    __git_complete_revlist
}

_git_stage ()
{
    _git_add
}

__git_config_get_set_variables ()
{
    local prevword word config_file= c=$cword
    while [ $c -gt 1 ]; do
	word="${words[c]}"
	case "$word" in
	    --system|--global|--local|--file=*)
		config_file="$word"
		break
		;;
	    -f|--file)
		config_file="$word $prevword"
		break
		;;
	    esac
	prevword=$word
	c=$((--c))
	done

    git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
    while read -r line
    do
	case "$line" in
	    *.*=*)
		echo "${line/=*/}"
		;;
	    esac
	done
}

_git_config ()
{
    case "$prev" in
	branch.*.remote|branch.*.pushremote)
	    __gitcomp_nl "$(__git_remotes)"
	    return
	    ;;
	branch.*.merge)
	    __gitcomp_nl "$(__git_refs)"
	    return
	    ;;
	branch.*.rebase)
	    __gitcomp "false true"
	    return
	    ;;
	remote.pushdefault)
	    __gitcomp_nl "$(__git_remotes)"
	    return
	    ;;
	remote.*.fetch)
	    local remote="${prev#remote.}"
	    remote="${remote%.fetch}"
	    if [ -z "$cur" ]; then
		__gitcomp_nl "refs/heads/" "" "" ""
		return
		fi
	    __gitcomp_nl "$(__git_refs_remotes "$remote")"
	    return
	    ;;
	remote.*.push)
	    local remote="${prev#remote.}"
	    remote="${remote%.push}"
	    __gitcomp_nl "$(git --git-dir="$(__gitdir)" \
for-each-ref --format='%(refname):%(refname)' \
refs/heads)"
	    return
	    ;;
	pull.twohead|pull.octopus)
	    __git_compute_merge_strategies
	    __gitcomp "$__git_merge_strategies"
	    return
	    ;;
	color.branch|color.diff|color.interactive|\
color.showbranch|color.status|color.ui)
	    __gitcomp "always never auto"
	    return
	    ;;
	color.pager)
	    __gitcomp "false true"
	    return
	    ;;
	color.*.*)
	    __gitcomp "
normal black red green yellow blue magenta cyan white
bold dim ul blink reverse
"
	    return
	    ;;
	diff.submodule)
	    __gitcomp "log short"
	    return
	    ;;
	help.format)
	    __gitcomp "man info web html"
	    return
	    ;;
	log.date)
	    __gitcomp "$__git_log_date_formats"
	    return
	    ;;
	sendemail.aliasesfiletype)
	    __gitcomp "mutt mailrc pine elm gnus"
	    return
	    ;;
	sendemail.confirm)
	    __gitcomp "$__git_send_email_confirm_options"
	    return
	    ;;
	sendemail.suppresscc)
	    __gitcomp "$__git_send_email_suppresscc_options"
	    return
	    ;;
	sendemail.transferencoding)
	    __gitcomp "7bit 8bit quoted-printable base64"
	    return
	    ;;
	--get|--get-all|--unset|--unset-all)
	    __gitcomp_nl "$(__git_config_get_set_variables)"
	    return
	    ;;
	*.*)
	    return
	    ;;
	esac
    case "$cur" in
	--*)
	    __gitcomp "
--system --global --local --file=
--list --replace-all
--get --get-all --get-regexp
--add --unset --unset-all
--remove-section --rename-section
"
	    return
	    ;;
	branch.*.*)
	    local pfx="${cur%.*}." cur_="${cur##*.}"
	    __gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_"
	    return
	    ;;
	branch.*)
	    local pfx="${cur%.*}." cur_="${cur#*.}"
	    __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
	    __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
	    return
	    ;;
	guitool.*.*)
	    local pfx="${cur%.*}." cur_="${cur##*.}"
	    __gitcomp "
argprompt cmd confirm needsfile noconsole norescan
prompt revprompt revunmerged title
" "$pfx" "$cur_"
	    return
	    ;;
	difftool.*.*)
	    local pfx="${cur%.*}." cur_="${cur##*.}"
	    __gitcomp "cmd path" "$pfx" "$cur_"
	    return
	    ;;
	man.*.*)
	    local pfx="${cur%.*}." cur_="${cur##*.}"
	    __gitcomp "cmd path" "$pfx" "$cur_"
	    return
	    ;;
	mergetool.*.*)
	    local pfx="${cur%.*}." cur_="${cur##*.}"
	    __gitcomp "cmd path trustExitCode" "$pfx" "$cur_"
	    return
	    ;;
	pager.*)
	    local pfx="${cur%.*}." cur_="${cur#*.}"
	    __git_compute_all_commands
	    __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_"
	    return
	    ;;
	remote.*.*)
	    local pfx="${cur%.*}." cur_="${cur##*.}"
	    __gitcomp "
url proxy fetch push mirror skipDefaultUpdate
receivepack uploadpack tagopt pushurl
" "$pfx" "$cur_"
	    return
	    ;;
	remote.*)
	    local pfx="${cur%.*}." cur_="${cur#*.}"
	    __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
	    __gitcomp_nl_append "pushdefault" "$pfx" "$cur_"
	    return
	    ;;
	url.*.*)
	    local pfx="${cur%.*}." cur_="${cur##*.}"
	    __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
	    return
	    ;;
	esac
    __gitcomp "
add.ignoreErrors
advice.commitBeforeMerge
advice.detachedHead
advice.implicitIdentity
advice.pushNonFastForward
advice.resolveConflict
advice.statusHints
alias.
am.keepcr
apply.ignorewhitespace
apply.whitespace
branch.autosetupmerge
branch.autosetuprebase
browser.
clean.requireForce
color.branch
color.branch.current
color.branch.local
color.branch.plain
color.branch.remote
color.decorate.HEAD
color.decorate.branch
color.decorate.remoteBranch
color.decorate.stash
color.decorate.tag
color.diff
color.diff.commit
color.diff.frag
color.diff.func
color.diff.meta
color.diff.new
color.diff.old
color.diff.plain
color.diff.whitespace
color.grep
color.grep.context
color.grep.filename
color.grep.function
color.grep.linenumber
color.grep.match
color.grep.selected
color.grep.separator
color.interactive
color.interactive.error
color.interactive.header
color.interactive.help
color.interactive.prompt
color.pager
color.showbranch
color.status
color.status.added
color.status.changed
color.status.header
color.status.nobranch
color.status.unmerged
color.status.untracked
color.status.updated
color.ui
commit.status
commit.template
core.abbrev
core.askpass
core.attributesfile
core.autocrlf
core.bare
core.bigFileThreshold
core.compression
core.createObject
core.deltaBaseCacheLimit
core.editor
core.eol
core.excludesfile
core.fileMode
core.fsyncobjectfiles
core.gitProxy
core.ignoreStat
core.ignorecase
core.logAllRefUpdates
core.loosecompression
core.notesRef
core.packedGitLimit
core.packedGitWindowSize
core.pager
core.preferSymlinkRefs
core.preloadindex
core.quotepath
core.repositoryFormatVersion
core.safecrlf
core.sharedRepository
core.sparseCheckout
core.symlinks
core.trustctime
core.warnAmbiguousRefs
core.whitespace
core.worktree
diff.autorefreshindex
diff.external
diff.ignoreSubmodules
diff.mnemonicprefix
diff.noprefix
diff.renameLimit
diff.renames
diff.statGraphWidth
diff.submodule
diff.suppressBlankEmpty
diff.tool
diff.wordRegex
diff.algorithm
difftool.
difftool.prompt
fetch.recurseSubmodules
fetch.unpackLimit
format.attach
format.cc
format.coverLetter
format.headers
format.numbered
format.pretty
format.signature
format.signoff
format.subjectprefix
format.suffix
format.thread
format.to
gc.
gc.aggressiveWindow
gc.auto
gc.autopacklimit
gc.packrefs
gc.pruneexpire
gc.reflogexpire
gc.reflogexpireunreachable
gc.rerereresolved
gc.rerereunresolved
gitcvs.allbinary
gitcvs.commitmsgannotation
gitcvs.dbTableNamePrefix
gitcvs.dbdriver
gitcvs.dbname
gitcvs.dbpass
gitcvs.dbuser
gitcvs.enabled
gitcvs.logfile
gitcvs.usecrlfattr
guitool.
gui.blamehistoryctx
gui.commitmsgwidth
gui.copyblamethreshold
gui.diffcontext
gui.encoding
gui.fastcopyblame
gui.matchtrackingbranch
gui.newbranchtemplate
gui.pruneduringfetch
gui.spellingdictionary
gui.trustmtime
help.autocorrect
help.browser
help.format
http.lowSpeedLimit
http.lowSpeedTime
http.maxRequests
http.minSessions
http.noEPSV
http.postBuffer
http.proxy
http.sslCAInfo
http.sslCAPath
http.sslCert
http.sslCertPasswordProtected
http.sslKey
http.sslVerify
http.useragent
i18n.commitEncoding
i18n.logOutputEncoding
imap.authMethod
imap.folder
imap.host
imap.pass
imap.port
imap.preformattedHTML
imap.sslverify
imap.tunnel
imap.user
init.templatedir
instaweb.browser
instaweb.httpd
instaweb.local
instaweb.modulepath
instaweb.port
interactive.singlekey
log.date
log.decorate
log.showroot
mailmap.file
man.
man.viewer
merge.
merge.conflictstyle
merge.log
merge.renameLimit
merge.renormalize
merge.stat
merge.tool
merge.verbosity
mergetool.
mergetool.keepBackup
mergetool.keepTemporaries
mergetool.prompt
notes.displayRef
notes.rewrite.
notes.rewrite.amend
notes.rewrite.rebase
notes.rewriteMode
notes.rewriteRef
pack.compression
pack.deltaCacheLimit
pack.deltaCacheSize
pack.depth
pack.indexVersion
pack.packSizeLimit
pack.threads
pack.window
pack.windowMemory
pager.
pretty.
pull.octopus
pull.twohead
push.default
push.followTags
rebase.autosquash
rebase.stat
receive.autogc
receive.denyCurrentBranch
receive.denyDeleteCurrent
receive.denyDeletes
receive.denyNonFastForwards
receive.fsckObjects
receive.unpackLimit
receive.updateserverinfo
remote.pushdefault
remotes.
repack.usedeltabaseoffset
rerere.autoupdate
rerere.enabled
sendemail.
sendemail.aliasesfile
sendemail.aliasfiletype
sendemail.bcc
sendemail.cc
sendemail.cccmd
sendemail.chainreplyto
sendemail.confirm
sendemail.envelopesender
sendemail.from
sendemail.identity
sendemail.multiedit
sendemail.signedoffbycc
sendemail.smtpdomain
sendemail.smtpencryption
sendemail.smtppass
sendemail.smtpserver
sendemail.smtpserveroption
sendemail.smtpserverport
sendemail.smtpuser
sendemail.suppresscc
sendemail.suppressfrom
sendemail.thread
sendemail.to
sendemail.validate
showbranch.default
status.relativePaths
status.showUntrackedFiles
status.submodulesummary
submodule.
tar.umask
transfer.unpackLimit
url.
user.email
user.name
user.signingkey
web.browser
branch. remote.
"
}

_git_remote ()
{
    local subcommands="add rename remove set-head set-branches set-url show prune update"
    local subcommand="$(__git_find_on_cmdline "$subcommands")"
    if [ -z "$subcommand" ]; then
	__gitcomp "$subcommands"
	return
	fi

    case "$subcommand" in
	rename|remove|set-url|show|prune)
	    __gitcomp_nl "$(__git_remotes)"
	    ;;
	set-head|set-branches)
	    __git_complete_remote_or_refspec
	    ;;
	update)
	    local i c='' IFS=$'\n'
	    for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
		i="${i#remotes.}"
		c="$c ${i/ */}"
		done
	    __gitcomp "$c"
	    ;;
	*)
	    ;;
	esac
}

_git_replace ()
{
    __gitcomp_nl "$(__git_refs)"
}

_git_reset ()
{
    __git_has_doubledash && return

    case "$cur" in
	--*)
	    __gitcomp "--merge --mixed --hard --soft --patch"
	    return
	    ;;
	esac
    __gitcomp_nl "$(__git_refs)"
}

_git_revert ()
{
    case "$cur" in
	--*)
	    __gitcomp "--edit --mainline --no-edit --no-commit --signoff"
	    return
	    ;;
	esac
    __gitcomp_nl "$(__git_refs)"
}

_git_rm ()
{
    case "$cur" in
	--*)
	    __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
	    return
	    ;;
	esac

    __git_complete_index_file "--cached"
}

_git_shortlog ()
{
    __git_has_doubledash && return

    case "$cur" in
	--*)
	    __gitcomp "
$__git_log_common_options
$__git_log_shortlog_options
--numbered --summary
"
	    return
	    ;;
	esac
    __git_complete_revlist
}

_git_show ()
{
    __git_has_doubledash && return

    case "$cur" in
	--pretty=*|--format=*)
	    __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
" "" "${cur#*=}"
	    return
	    ;;
	--diff-algorithm=*)
	    __gitcomp "$__git_diff_algorithms" "" "${cur##--diff-algorithm=}"
	    return
	    ;;
	--*)
	    __gitcomp "--pretty= --format= --abbrev-commit --oneline
--show-signature
$__git_diff_common_options
"
	    return
	    ;;
	esac
    __git_complete_revlist_file
}

_git_show_branch ()
{
    case "$cur" in
	--*)
	    __gitcomp "
--all --remotes --topo-order --current --more=
--list --independent --merge-base --no-name
--color --no-color
--sha1-name --sparse --topics --reflog
"
	    return
	    ;;
	esac
    __git_complete_revlist
}

_git_stash ()
{
    local save_opts='--keep-index --no-keep-index --quiet --patch'
    local subcommands='save list show apply clear drop pop create branch'
    local subcommand="$(__git_find_on_cmdline "$subcommands")"
    if [ -z "$subcommand" ]; then
	case "$cur" in
	    --*)
		__gitcomp "$save_opts"
		;;
	    *)
		if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
		    __gitcomp "$subcommands"
		    fi
		;;
	    esac
	else
	case "$subcommand,$cur" in
	    save,--*)
		__gitcomp "$save_opts"
		;;
	    apply,--*|pop,--*)
		__gitcomp "--index --quiet"
		;;
	    show,--*|drop,--*|branch,--*)
		;;
	    show,*|apply,*|drop,*|pop,*|branch,*)
		__gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
| sed -n -e 's/:.*//p')"
		;;
	    *)
		;;
	    esac
	fi
}

_git_submodule ()
{
    __git_has_doubledash && return

    local subcommands="add status init deinit update summary foreach sync"
    if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
	case "$cur" in
	    --*)
		__gitcomp "--quiet --cached"
		;;
	    *)
		__gitcomp "$subcommands"
		;;
	    esac
	return
	fi
}

_git_svn ()
{
    local subcommands="
init fetch clone rebase dcommit log find-rev
set-tree commit-diff info create-ignore propget
proplist show-ignore show-externals branch tag blame
migrate mkdirs reset gc
"
    local subcommand="$(__git_find_on_cmdline "$subcommands")"
    if [ -z "$subcommand" ]; then
	__gitcomp "$subcommands"
	else
	local remote_opts="--username= --config-dir= --no-auth-cache"
	local fc_opts="
--follow-parent --authors-file= --repack=
--no-metadata --use-svm-props --use-svnsync-props
--log-window-size= --no-checkout --quiet
--repack-flags --use-log-author --localtime
--ignore-paths= --include-paths= $remote_opts
"
	local init_opts="
--template= --shared= --trunk= --tags=
--branches= --stdlayout --minimize-url
--no-metadata --use-svm-props --use-svnsync-props
--rewrite-root= --prefix= --use-log-author
--add-author-from $remote_opts
"
	local cmt_opts="
--edit --rmdir --find-copies-harder --copy-similarity=
"

	case "$subcommand,$cur" in
	    fetch,--*)
		__gitcomp "--revision= --fetch-all $fc_opts"
		;;
	    clone,--*)
		__gitcomp "--revision= $fc_opts $init_opts"
		;;
	    init,--*)
		__gitcomp "$init_opts"
		;;
	    dcommit,--*)
		__gitcomp "
--merge --strategy= --verbose --dry-run
--fetch-all --no-rebase --commit-url
--revision --interactive $cmt_opts $fc_opts
"
		;;
	    set-tree,--*)
		__gitcomp "--stdin $cmt_opts $fc_opts"
		;;
	    create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
show-externals,--*|mkdirs,--*)
		__gitcomp "--revision="
		;;
	    log,--*)
		__gitcomp "
--limit= --revision= --verbose --incremental
--oneline --show-commit --non-recursive
--authors-file= --color
"
		;;
	    rebase,--*)
		__gitcomp "
--merge --verbose --strategy= --local
--fetch-all --dry-run $fc_opts
"
		;;
	    commit-diff,--*)
		__gitcomp "--message= --file= --revision= $cmt_opts"
		;;
	    info,--*)
		__gitcomp "--url"
		;;
	    branch,--*)
		__gitcomp "--dry-run --message --tag"
		;;
	    tag,--*)
		__gitcomp "--dry-run --message"
		;;
	    blame,--*)
		__gitcomp "--git-format"
		;;
	    migrate,--*)
		__gitcomp "
--config-dir= --ignore-paths= --minimize
--no-auth-cache --username=
"
		;;
	    reset,--*)
		__gitcomp "--revision= --parent"
		;;
	    *)
		;;
	    esac
	fi
}

_git_tag ()
{
    local i c=1 f=0
    while [ $c -lt $cword ]; do
	i="${words[c]}"
	case "$i" in
	    -d|-v)
		__gitcomp_nl "$(__git_tags)"
		return
		;;
	    -f)
		f=1
		;;
	    esac
	((c++))
	done

    case "$prev" in
	-m|-F)
	    ;;
	-*|tag)
	    if [ $f = 1 ]; then
		__gitcomp_nl "$(__git_tags)"
		fi
	    ;;
	*)
	    __gitcomp_nl "$(__git_refs)"
	    ;;
	esac

    case "$cur" in
	--*)
	    __gitcomp "
--list --delete --verify --annotate --message --file
--sign --cleanup --local-user --force --column --sort
--contains --points-at
"
	    ;;
	esac
}

_git_whatchanged ()
{
    _git_log
}

__git_main ()
{
    local i c=1 command __git_dir

    while [ $c -lt $cword ]; do
	i="${words[c]}"
	case "$i" in
	    --git-dir=*) __git_dir="${i#--git-dir=}" ;;
	    --git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
	    --bare)      __git_dir="." ;;
	    --help) command="help"; break ;;
	    -c|--work-tree|--namespace) ((c++)) ;;
	    -*) ;;
	    *) command="$i"; break ;;
	    esac
	((c++))
	done

    if [ -z "$command" ]; then
	case "$cur" in
	    --*)   __gitcomp "
--paginate
--no-pager
--git-dir=
--bare
--version
--exec-path
--exec-path=
--html-path
--man-path
--info-path
--work-tree=
--namespace=
--no-replace-objects
--help
"
		;;
	    *)     __git_compute_porcelain_commands
		       __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
	    esac
	return
	fi

    local completion_func="_git_${command//-/_}"
    declare -f $completion_func >/dev/null && $completion_func && return

    local expansion=$(__git_aliased_command "$command")
    if [ -n "$expansion" ]; then
	words[1]=$expansion
	completion_func="_git_${expansion//-/_}"
	declare -f $completion_func >/dev/null && $completion_func
	fi
}

__gitk_main ()
{
    __git_has_doubledash && return

    local g="$(__gitdir)"
    local merge=""
    if [ -f "$g/MERGE_HEAD" ]; then
	merge="--merge"
	fi
    case "$cur" in
	--*)
	    __gitcomp "
$__git_log_common_options
$__git_log_gitk_options
$merge
"
	    return
	    ;;
	esac
    __git_complete_revlist
}

if [[ -n ${ZSH_VERSION-} ]]; then
    echo "WARNING: this script is deprecated, please see git-completion.zsh" 1>&2

    autoload -U +X compinit && compinit

    __gitcomp ()
    {
	emulate -L zsh

	local cur_="${3-$cur}"

	case "$cur_" in
	    --*=)
		;;
	    *)
		local c IFS=$' \t\n'
		local -a array
		for c in ${=1}; do
		    c="$c${4-}"
		    case $c in
			--*=*|*.) ;;
			*) c="$c " ;;
			esac
		    array[${#array[@]}+1]="$c"
		    done
		compset -P '*[=:]'
		compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
		;;
	    esac
	}

    __gitcomp_nl ()
    {
	emulate -L zsh

	local IFS=$'\n'
	compset -P '*[=:]'
	compadd -Q -S "${4- }" -p "${2-}" -- ${=1} && _ret=0
	}

    __gitcomp_file ()
    {
	emulate -L zsh

	local IFS=$'\n'
	compset -P '*[=:]'
	compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
	}

    _git ()
    {
	local _ret=1 cur cword prev
	cur=${words[CURRENT]}
	prev=${words[CURRENT-1]}
	let cword=CURRENT-1
	emulate ksh -c __${service}_main
	let _ret && _default && _ret=0
	return _ret
	}

    compdef _git git gitk
    return
fi

__git_func_wrap ()
{
    local cur words cword prev
    _get_comp_words_by_ref -n =: cur words cword prev
    $1
}

# Setup completion for certain functions defined above by setting common
# variables and workarounds.
# This is NOT a public function; use at your own risk.
__git_complete ()
{
    local wrapper="__git_wrap${2}"
    eval "$wrapper () { __git_func_wrap $2 ; }"
    complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \
	|| complete -o default -o nospace -F $wrapper $1
}

# wrapper for backwards compatibility
_git ()
{
    __git_wrap__git_main
}

# wrapper for backwards compatibility
_gitk ()
{
    __git_wrap__gitk_main
}

__git_complete git __git_main
__git_complete gitk __gitk_main

# The following are necessary only for Cygwin, and only are needed
# when the user has tab-completed the executable name and consequently
# included the '.exe' suffix.
#
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
__git_complete git.exe __git_main
fi
